home *** CD-ROM | disk | FTP | other *** search
/ Ham Radio 2000 #1 / Ham Radio 2000.iso / ham2000 / tcp_ip / tnos / tnos100s / ftpcli.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-12-17  |  43.0 KB  |  1,847 lines

  1. /* Internet FTP client (interactive user)
  2.  * Copyright 1991 Phil Karn, KA9Q
  3.  */
  4.  /* Mods by G1EMM and PA0GRI */
  5. /* modifications for encrypted password by ik1che 900419 */
  6. /* added "resume" and "rput" commands for interrupted file transfers
  7.  * by iw0cnb 15 Feb 92 */
  8.  
  9. #include <stdio.h>
  10. #include "global.h"
  11. #include "config.h"
  12. #include "mbuf.h"
  13. #include "session.h"
  14. #include "cmdparse.h"
  15. #include "proc.h"
  16. #include "tty.h"
  17. #include "socket.h"
  18. #include "ftp.h"
  19. #include "ftpcli.h"
  20. #include "commands.h"
  21. #include "netuser.h"
  22. #include "dirutil.h"
  23. #include "help.h"
  24. #ifdef LZW
  25. #include "lzw.h"
  26. #endif
  27.  
  28. #define    DIRBUF    256
  29.  
  30. extern char *FTPHelp;           /* FTP Help file */
  31.  
  32. #ifdef ALLSESSIONS
  33. static int doascii __ARGS((int argc,char *argv[],void *p));
  34. static int dobatch __ARGS((int argc,char *argv[],void *p));
  35. static int dobinary __ARGS((int argc,char *argv[],void *p));
  36. static int docompare __ARGS((int argc,char *argv[],void *p));
  37. static int doftpcd __ARGS((int argc,char *argv[],void *p));
  38. static int doftpcdup __ARGS((int argc,char *argv[],void *p));
  39. static int doftpdel __ARGS((int argc,char *argv[],void *p));
  40. static int doget __ARGS((int argc,char *argv[],void *p));
  41. static int dohash __ARGS((int argc,char *argv[],void *p));
  42. static int doverbose __ARGS((int argc,char *argv[],void *p));
  43. static int dolist __ARGS((int argc,char *argv[],void *p));
  44. static int dols __ARGS((int argc,char *argv[],void *p));
  45. static int domd5 __ARGS((int argc,char *argv[],void *p));
  46. static int doldir  __ARGS((int argc,char *argv[],void *p));
  47. static int dolcd  __ARGS((int argc,char *argv[],void *p));
  48. static int dolmkdir __ARGS((int argc,char *argv[],void *p));
  49. static int dolrename __ARGS((int argc,char *argv[],void *p));
  50. static int dolrmdir __ARGS((int argc,char *argv[],void *p));
  51. static int domcompare __ARGS((int argc,char *argv[],void *p));
  52. static int domkdir __ARGS((int argc,char *argv[],void *p));
  53. static int domget __ARGS((int argc,char *argv[],void *p));
  54. static int domput __ARGS((int argc,char *argv[],void *p));
  55. static int doput __ARGS((int argc,char *argv[],void *p));
  56. static int dopwd __ARGS((int argc,char *argv[],void *p));
  57. static int doftphelp __ARGS((int argc,char *argv[],void *p));
  58. static int doquit __ARGS((int argc,char *argv[],void *p));
  59. static int dormdir __ARGS((int argc,char *argv[],void *p));
  60. static int dorename __ARGS((int argc,char *argv[],void *p));
  61. static int doresume __ARGS((int argc,char *argv[],void *p));
  62. static int dorput __ARGS((int argc,char *argv[],void *p));
  63. static int dotype __ARGS((int argc,char *argv[],void *p));
  64. static int getline __ARGS((struct session *sp,char *prompt,char *buf,int n));
  65. static int getresp __ARGS((struct ftpcli *ftp,int mincode));
  66. static int doupdate __ARGS((int argc,char *argv[],void *p));
  67. static long getsub __ARGS((struct ftpcli *ftp,char *command,char *remotename,
  68.     char *localname));
  69. static long putsub __ARGS((struct ftpcli *ftp,char *remotename,char *localname,int putr));
  70. static void sendport __ARGS((int s,struct sockaddr_in *socket));
  71.  
  72. static char Notsess[] = "Not an FTP session!\n";
  73. static int Ftp_type = ASCII_TYPE;
  74. static int Ftp_logbsize = 8;
  75.  
  76. static struct cmds DFAR Ftpcmds[] = {
  77.     "",        donothing,    0, 0, NULLCHAR,
  78.     "?",        doftphelp,    0, 0, NULLCHAR,
  79.     "append",    doput,        0, 2, "append <localfile> <remotefile>",
  80.     "ascii",    doascii,    0, 0, NULLCHAR,
  81.     "batch",    dobatch,    0, 0, NULLCHAR,
  82.     "binary",    dobinary,    0, 0, NULLCHAR,
  83.     "bye",        doquit,        0, 0, NULLCHAR,
  84.     "cd",        doftpcd,    0, 2, "cd <directory>",
  85.     "cdup",        doftpcdup,    0, 0, NULLCHAR,
  86.     "compare",    docompare,    0, 2, "compare <remotefile> [<localfile>]",
  87.     "del",        doftpdel,    0, 2, "del <remotefile>",
  88.     "dir",        dolist,        0, 0, NULLCHAR,
  89.     "exit",        doquit,        0, 0, NULLCHAR,
  90.     "get",        doget,        0, 2, "get <remotefile> <localfile>",
  91.     "hash",        dohash,        0, 0, NULLCHAR,
  92.     "help",        doftphelp,    0, 0, NULLCHAR,
  93.     "lcd",          dolcd,          0, 1, NULLCHAR,
  94.     "ldir",         doldir,         0, 1, NULLCHAR,
  95.     "list",        dolist,        0, 0, NULLCHAR,
  96.     "lmkdir",       dolmkdir,       0, 2, "lmkdir <local Directory>",
  97.     "lrename",    dolrename,    0, 3, "lrename <oldname> <newname>",
  98.     "lrmdir",       dolrmdir,       0, 2, "lrmdir <local Directory>",
  99.     "ls",        dols,        0, 0, NULLCHAR,
  100. #ifdef LZW
  101.     "lzw",        dolzw,        0, 0, NULLCHAR,
  102. #endif
  103.     "mcompare",    domcompare,    0, 2, "mcompare <file> [<file> ...]",
  104.     "md",        domkdir,    0, 2, "md <directory>",
  105.     "md5",        domd5,        0, 2, "md5 <file>",
  106.     "mget",        domget,        0, 2, "mget <file> [<file> ...]",
  107.     "mkdir",    domkdir,    0, 2, "mkdir <directory>",
  108.     "mput",        domput,        0, 2, "mput <file> [<file> ...]",
  109.     "nlst",        dols,        0, 0, NULLCHAR,
  110.     "put",        doput,        0, 2, "put <localfile> <remotefile>",
  111.     "pwd",        dopwd,        0, 0, NULLCHAR,
  112.     "quit",        doquit,        0, 0, NULLCHAR,
  113.     "rename",    dorename,    0, 3, "rename <oldname> <newname>",
  114.     "resume",       doresume,       0, 2, "resume <remotefile> <localfile>",
  115.     "rmdir",    dormdir,    0, 2, "rmdir <directory>",
  116.     "rput",         dorput,         0, 2, "rput <localfile> <remotefile>",
  117.     "type",        dotype,        0, 0, NULLCHAR,
  118.     "update",    doupdate,    0, 0, NULLCHAR,
  119.     "verbose",    doverbose,    0, 0, NULLCHAR,
  120.     NULLCHAR,    NULLFP,        0, 0, NULLCHAR,
  121. };
  122.  
  123.  
  124. int
  125. doftphelp(argc,argv,p)
  126. int argc;
  127. char *argv[];
  128. void *p;
  129. {
  130.     dohelper ("FTP commands:\n", &Ftpcmds[1], NULLCHAR, (argv[0][0] == '?' && argc == 1) ? NULLCHAR : FTPHelp, (argc == 2) ? argv[1] : NULLCHAR);
  131.     return 0;
  132. }
  133.  
  134. /* Handle top-level FTP command */
  135. int
  136. doftp(argc,argv,p)
  137. int argc;
  138. char *argv[];
  139. void *p;
  140. {
  141.     struct session *sp;
  142.     struct ftpcli ftp;
  143.     struct sockaddr_in fsocket;
  144.     int resp,vsave;
  145.     char *buf,*bufsav,*cp,*un;
  146.     char prmt[40];
  147.     char l[17];
  148.     int control;
  149.     char *ftpcli_login();
  150.     FILE *fp1 = NULLFILE;
  151.     struct  cur_dirs dirs;
  152.  
  153.     /*Make sure this comes from console - WG7J*/
  154.     if(Curproc->input != Command->input)
  155.         return 0;
  156.  
  157.     /* Allocate a session control block */
  158.     if((sp = newsession(argv[1],FTP,0)) == NULLSESSION){
  159.         tputs(TooManySessions);
  160.         return 1;
  161.     }
  162.     memset((char *)&ftp,0,sizeof(ftp));
  163.     ftp.control = ftp.data = -1;
  164.     ftp.verbose = V_BYTE;    /* changed to ver 4 default - KO4KS */
  165.     ftp.type = Ftp_type;
  166.     ftp.logbsize = Ftp_logbsize;
  167.  
  168.     sp->cb.ftp = &ftp;    /* Downward link */
  169.     ftp.session = sp;    /* Upward link */
  170.  
  171.     ftp.curdirs = &dirs;      
  172.  
  173.     fsocket.sin_family = AF_INET;
  174.     fsocket.sin_port = IPPORT_FTP;
  175.  
  176.     tprintf("Resolving %s... ",sp->name);
  177.     if((fsocket.sin_addr.s_addr = resolve(sp->name)) == 0){
  178.         tprintf(Badhost,sp->name);
  179.         keywait(NULLCHAR,1);
  180.         freesession(sp);
  181.         return 1;
  182.     }
  183.     /* Open the control connection */
  184.     if((control = sp->s = ftp.control = socket(AF_INET,SOCK_STREAM,0)) == -1){
  185.         tputs(Nosock);
  186.         keywait(NULLCHAR,1);
  187.         freesession(sp);
  188.         return 1;
  189.     }
  190.     sockmode(sp->s,SOCK_ASCII);
  191.     setflush(sp->s,-1);    /* Flush output only when we call getresp() */
  192.     tprintf("Trying %s...\n",psocket((struct sockaddr *)&fsocket));
  193.     tprintf("Local Directory - %s\n",init_dirs(&dirs));
  194.     if(connect(control,(char *)&fsocket,sizeof(fsocket)) == -1)
  195.         goto quit;
  196.     tprintf("FTP session %u connected to %s\n",(unsigned)(sp-Sessions),
  197.         sp->name);
  198.  
  199.     /* Wait for greeting from server */
  200.     resp = getresp(&ftp,200);
  201.  
  202.     if(resp >= 400)
  203.         goto quit;
  204.     un = getenv("USER");
  205.         if(un != NULLCHAR)
  206.             sprintf(prmt,"Enter user name (%s): ",un);
  207.         else
  208.             sprintf(prmt,"Enter user name: ");
  209.     /* Now process responses and commands */
  210.     buf = mallocw(LINELEN);
  211.  
  212.     if(argc > 2)
  213.         if((fp1 = fopen(argv[2],READ_TEXT)) == NULLFILE) goto quit;
  214.     while(resp != -1){
  215.         switch (resp)    {
  216.             case 220:    /* Sign-on banner; prompt for and send USER command */
  217.                     if((cp = ftpcli_login(&ftp, sp->name)) == NULLCHAR)    {
  218.                         if(argc > 2)    {
  219.                             if(fgets(buf,LINELEN,fp1) == NULLCHAR)
  220.                                 goto quit;
  221.                         } else
  222.                             getline(sp,prmt,buf,LINELEN);
  223.                         /* Send the command only if the user response
  224.                          * was non-null
  225.                          */
  226.                         if(buf[0] != '\n')    {
  227.                             usprintf(control,"USER %s",buf);
  228.                             resp = getresp(&ftp,200);
  229.                         } else {
  230.                             if(un != NULLCHAR)    {
  231.                                 usprintf(control,"USER %s\n",un);
  232.                                 resp = getresp(&ftp,200);
  233.                             } else {
  234.                                 tprintf("No username sent\n");
  235.                                 resp = 200;    /* dummy */
  236.                             }
  237.                         }
  238.                     } else {
  239.                         usprintf(control,"USER %s\n",cp);
  240.                         free(cp);
  241.                         resp = getresp(&ftp,200);
  242.                     }
  243.                     break;
  244.             case 331:    if(ftp.password == NULLCHAR)    {
  245.                         /* turn off echo */
  246.                         if(argc > 2)    {
  247.                             if(fgets(buf,LINELEN,fp1) == NULLCHAR)
  248.                                 goto quit;
  249.                         } else    {
  250.                             sp->ttystate.echo = 0;
  251.                             getline(sp,"Password: ",buf,LINELEN);
  252.                             tprintf("\n");
  253.                             /* Turn echo back on */
  254.                             sp->ttystate.echo = 1;
  255.                         }
  256.                         /* Send the command only if the user response
  257.                          * was non-null
  258.                          */
  259.                         if(buf[0] != '\n')    {
  260.                             usprintf(control,"PASS %s",buf);
  261.                             resp = getresp(&ftp,200);
  262.                         } else {
  263.                             tprintf("Password must be provided.\nLoggin failed.\n");
  264.                             resp = 200;    /* dummy */
  265.                         }
  266.                     } else {
  267.                         usprintf(control,"PASS %s\n",ftp.password);
  268.                         resp = getresp(&ftp,200);
  269.                         free(ftp.password);
  270.                         ftp.password = NULLCHAR;    /* clean up */
  271.                     }
  272.                     break;
  273.             case 230:    /* Successful login */
  274.                     /* Find out what type of system we're talking to */
  275.                     tprintf("ftp> syst\n");
  276.                     usprintf(control,"SYST\n");
  277.                     resp = getresp(&ftp,200);
  278.                     break;
  279.             case 215:    /* Response to SYST command */
  280.                     cp = strchr(ftp.line,' ');
  281.                     if(cp != NULLCHAR && strnicmp(cp+1,System,strlen(System)) == 0){
  282.                         ftp.type = IMAGE_TYPE;
  283.                         tprintf("Defaulting to binary mode\n");
  284.                     }
  285.                     resp = 200;    /* dummy */
  286.                     break;
  287. #ifdef notyet
  288.             case 399:    /* Encrypted password login */
  289.                     if(ftp.password == NULLCHAR)    {
  290.                         getline(sp,"Key ? --> ",buf,LINELEN);
  291.                         /* Send the command only if the user response
  292.                          * was non-null
  293.                          */
  294.                         if(buf[0] != '\n')    {
  295.                             cp = strchr(ftp.line,':');
  296.                             cp += 2;
  297.                               /*  
  298.                             epass(htol(cp),buf,l);
  299.                               */  
  300.                             l[16] = '\0';
  301.                             free(ftp.line);
  302.                             ftp.line = NULLCHAR;
  303.                             usprintf(control,"PASS %s\n",l);
  304.                             resp = getresp(&ftp,200);
  305.                         } else {
  306.                             tprintf("Password must be provided.\nLogin failed.\n");
  307.                             resp = 200;     /* dummy */
  308.                         }
  309.                     } else {
  310.                         cp = strchr(ftp.line,':');
  311.                         cp += 2;
  312.                         /*
  313.                         epass(htol(cp),ftp.password,l);
  314.                         */
  315.                         l[16] = '\0';
  316.                         free(ftp.line);
  317.                         ftp.line = NULLCHAR;
  318.                         usprintf(control,"PASS %s\n",l);
  319.                         resp = getresp(&ftp,200);
  320.                         free(ftp.password);
  321.                         ftp.password = NULLCHAR;        /* clean up */
  322.                     }
  323.                     break;
  324. #endif
  325.             default:    /* Test the control channel first */
  326.                     if(sockstate(control) == NULLCHAR)
  327.                         break;
  328.                     if(argc > 2)    {
  329.                         if(fgets(buf,LINELEN,fp1) == NULLCHAR)
  330.                             goto quit;
  331.                     } else
  332.                         getline(sp,"ftp> ",buf,LINELEN);
  333.  
  334.                     /* Copy because cmdparse modifies the original */
  335.                     bufsav = strdup(buf);
  336.                     if((resp = cmdparse(Ftpcmds,buf,&ftp)) != -1)    {
  337.                         /* Valid command, free buffer and get another */
  338.                         free(bufsav);
  339.                     } else {
  340.                         /* Not a local cmd, send to remote server */
  341.                         usputs(control,bufsav);
  342.                         free(bufsav);
  343.  
  344.                         /* Enable display of server response */
  345.                         vsave = ftp.verbose;
  346.                         ftp.verbose = V_NORMAL;
  347.                         resp = getresp(&ftp,200);
  348.                         ftp.verbose = vsave;
  349.                     }
  350.             }
  351.     }
  352.     free(buf);
  353. quit:    cp = sockerr(control);
  354.     tprintf("FTP session %u closed: %s\n",(unsigned)(sp - Sessions),
  355.      cp != NULLCHAR ? cp : "EOF");
  356.  
  357.     if(ftp.fp != NULLFILE && ftp.fp != stdout)
  358.         fclose(ftp.fp);
  359.     if(ftp.data != -1)
  360.         close_s(ftp.data);
  361.     if(ftp.control != -1)
  362.         close_s(ftp.control);
  363.     if(argc < 3)
  364.         keywait(NULLCHAR,1);
  365.     else
  366.         fclose(fp1);
  367.     if(ftp.session != NULLSESSION)
  368.         freesession(ftp.session);
  369.     free_dirs(&dirs);         
  370.     return 0;
  371. }
  372.  
  373.  
  374. /* Control verbosity level */
  375. static int
  376. doverbose(argc,argv,p)
  377. int argc;
  378. char *argv[];
  379. void *p;
  380. {
  381.     register struct ftpcli *ftp;
  382.  
  383.     if((ftp = (struct ftpcli *)p) == NULLFTP)
  384.         return -1;
  385.     return setshort(&ftp->verbose,"Verbose",argc,argv);
  386. }
  387. /* Enable/disable command batching */
  388. static int
  389. dobatch(argc,argv,p)
  390. int argc;
  391. char *argv[];
  392. void *p;
  393. {
  394.     register struct ftpcli *ftp;
  395.  
  396.     if((ftp = (struct ftpcli *)p) == NULLFTP)
  397.         return -1;
  398.     return setbool(&ftp->batch,"Command batching",argc,argv);
  399. }
  400. /* Enable/disable update flag */
  401. static int
  402. doupdate(argc,argv,p)
  403. int argc;
  404. char *argv[];
  405. void *p;
  406. {
  407.     register struct ftpcli *ftp;
  408.  
  409.     if((ftp = (struct ftpcli *)p) == NULLFTP)
  410.         return -1;
  411.     return setbool(&ftp->update,"Update with MD5",argc,argv);
  412. }
  413. /* Set verbosity to high (convenience command) */
  414. static int
  415. dohash(argc,argv,p)
  416. int argc;
  417. char *argv[];
  418. void *p;
  419. {
  420.     register struct ftpcli *ftp;
  421.  
  422.     if((ftp = (struct ftpcli *)p) == NULLFTP)
  423.         return -1;
  424.     tputs("Hash Printing ");
  425.     if (ftp->verbose==V_HASH){
  426.         ftp->verbose = V_HASH+1;
  427.         tputs("Off\n");
  428.     } else {
  429.         tputs("On\n");
  430.         ftp->verbose = V_HASH;
  431.     }
  432.     return 0;
  433. }
  434.     
  435. /* Close session */
  436. static int
  437. doquit(argc,argv,p)
  438. int argc;
  439. char *argv[];
  440. void *p;
  441. {
  442.     register struct ftpcli *ftp;
  443.  
  444.     ftp = (struct ftpcli *)p;
  445.     if(ftp == NULLFTP)
  446.         return -1;
  447.     usprintf(ftp->control,"QUIT\n");
  448.     getresp(ftp,200);    /* Get the closing message */
  449.     getresp(ftp,200);    /* Wait for the server to close */
  450.     return -1;
  451. }
  452.  
  453. #ifdef LZW
  454. /* Toggle LZW compressed streams mode on */
  455. static int
  456. dolzw(argc,argv,p)
  457. int argc;
  458. char *argv[];
  459. void *p;
  460. {
  461. register struct ftpcli *ftp;
  462. int retval;
  463.  
  464.     ftp = (struct ftpcli *)p;
  465.     if(ftp == NULLFTP)
  466.         return -1;
  467.     if (ftp->lzw)    {
  468.         tprintf ("550 Already using LZW compression\n");
  469.         return (550);
  470.     }
  471.     usprintf(ftp->control,"XLZW %d %d\n", Lzwbits, Lzwmode);
  472.     retval = getresp(ftp,200);
  473.     if (retval >= 200 && retval < 300)     {
  474.         ftp->lzw = 1;
  475.         ftp->lzwbits = Lzwbits;
  476.         ftp->lzwmode = Lzwmode;
  477. /*        lzwinit (ftp->control, Lzwbits, Lzwmode);    */
  478.     }
  479.     return (retval);
  480. }
  481. #endif
  482.  
  483. /* Rename remote file */
  484. static int
  485. dorename(argc,argv,p)
  486. int argc;
  487. char *argv[];
  488. void *p;
  489. {
  490. register struct ftpcli *ftp;
  491. int retval;
  492.  
  493.     ftp = (struct ftpcli *)p;
  494.     if(ftp == NULLFTP)
  495.         return -1;
  496.     usprintf(ftp->control,"RNFR %s\n", argv[1]);
  497.     retval = getresp(ftp,350);
  498.  
  499.     if (retval != 350)
  500.         return (retval);
  501.     usprintf(ftp->control,"RNTO %s\n", argv[2]);
  502.     return (getresp(ftp,200));
  503. }
  504.  
  505. /* Rename local file */
  506. static int
  507. dolrename(argc,argv,p)
  508. int argc;
  509. char *argv[];
  510. void *p;
  511. {
  512. register struct ftpcli *ftp;
  513. char fname1[128];
  514. char fname2[128];
  515.  
  516.     ftp = (struct ftpcli *)p;
  517.     if(ftp == NULLFTP)
  518.         return -1;
  519.     strcpy(fname1,make_fname(ftp->curdirs->dir,argv[1]));
  520.     strcpy(fname2,make_fname(ftp->curdirs->dir,argv[2]));
  521.     if(rename(fname1,fname2) == -1)
  522.         tprintf("Can't rename: %s\n",sys_errlist[errno]);
  523.     else
  524.         tprintf("Local file renamed to '%s'\n",fname2);
  525.     return 0;
  526.  
  527. }
  528.  
  529. static int
  530. txlate (argc,argv,p,substr, parm)
  531. int argc;
  532. char *argv[];
  533. void *p;
  534. char *substr;
  535. int parm;
  536. {
  537. register struct ftpcli *ftp;
  538.  
  539.     ftp = (struct ftpcli *)p;
  540.     if(ftp == NULLFTP)
  541.         return -1;
  542.     usprintf(ftp->control,"%s %s\n",substr, (parm) ? argv[1] : "");
  543.     return getresp(ftp,200);
  544. }
  545.  
  546. /* Pass PWD to server - included here to allow cmd to be viewed in help list */
  547. static int
  548. dopwd(argc,argv,p)
  549. int argc;
  550. char *argv[];
  551. void *p;
  552. {
  553.     return (txlate (argc, argv, p, "PWD", 0));
  554. }
  555.  
  556. /* Translate 'cd' to 'cwd' for convenience */
  557. static int
  558. doftpcd(argc,argv,p)
  559. int argc;
  560. char *argv[];
  561. void *p;
  562. {
  563.     if (argc == 1)
  564.         return (dopwd(argc, argv, p));
  565.     return (txlate (argc, argv, p, "CWD", 1));
  566. }
  567.  
  568. /* Pass CDUP to server - included here to allow cmd to be viewed in help list */
  569. static int
  570. doftpcdup(argc,argv,p)
  571. int argc;
  572. char *argv[];
  573. void *p;
  574. {
  575.     return (txlate (argc, argv, p, "CDUP", 0));
  576. }
  577.  
  578. /* Translate 'del' to 'dele' for convenience */
  579. static int
  580. doftpdel(argc,argv,p)
  581. int argc;
  582. char *argv[];
  583. void *p;
  584. {
  585.     return (txlate (argc, argv, p, "DELE", 1));
  586. }
  587.  
  588. /* Translate 'mkdir' to 'xmkd' for convenience */
  589. static int
  590. domkdir(argc,argv,p)
  591. int argc;
  592. char *argv[];
  593. void *p;
  594. {
  595.     return (txlate (argc, argv, p, "XMKD", 1));
  596. }
  597. /* Translate 'rmdir' to 'xrmd' for convenience */
  598. static int
  599. dormdir(argc,argv,p)
  600. int argc;
  601. char *argv[];
  602. void *p;
  603. {
  604.     return (txlate (argc, argv, p, "XRMD", 1));
  605. }
  606. static int
  607. dobinary(argc,argv,p)
  608. int argc;
  609. char *argv[];
  610. void *p;
  611. {
  612.     char *args[2];
  613.  
  614.     args[1] = "I";
  615.     return dotype(2,args,p);
  616. }
  617. static int
  618. doascii(argc,argv,p)
  619. int argc;
  620. char *argv[];
  621. void *p;
  622. {
  623.     char *args[2];
  624.  
  625.     args[1] = "A";
  626.     return dotype(2,args,p);
  627. }
  628.  
  629. /* Handle "type" command from user */
  630. static int
  631. dotype(argc,argv,p)
  632. int argc;
  633. char *argv[];
  634. void *p;
  635. {
  636.     register struct ftpcli *ftp;
  637.  
  638.     ftp = (struct ftpcli *)p;
  639.     if(ftp == NULLFTP)
  640.         return -1;
  641.     if(argc < 2){
  642.         switch(ftp->type){
  643.         case IMAGE_TYPE:
  644.             tprintf("Image\n");
  645.             break;
  646.         case ASCII_TYPE:
  647.             tprintf("Ascii\n");
  648.             break;
  649.         case LOGICAL_TYPE:
  650.             tprintf("Logical bytesize %u\n",ftp->logbsize);
  651.             break;
  652.         }
  653.         return 0;
  654.     }
  655.     switch(*argv[1]){
  656.     case 'i':
  657.     case 'I':
  658.     case 'b':
  659.     case 'B':
  660.         ftp->type = IMAGE_TYPE;
  661.         break;
  662.     case 'a':
  663.     case 'A':
  664.         ftp->type = ASCII_TYPE;
  665.         break;
  666.     case 'L':
  667.     case 'l':
  668.         ftp->type = LOGICAL_TYPE;
  669.         ftp->logbsize = atoi(argv[2]);
  670.         break;
  671.     default:
  672.         tprintf("Invalid type %s\n",argv[1]);
  673.         return 1;
  674.     }
  675.     return 0;
  676. }
  677.  
  678. /* Handle "ftype" command */
  679. int
  680. doftype(argc,argv,p)
  681. int argc;
  682. char *argv[];
  683. void *p;
  684. {
  685.     if(argc < 2){
  686.         tprintf("Ftp initial TYPE is ");
  687.         switch(Ftp_type){
  688.         case IMAGE_TYPE:
  689.             tprintf("Image\n");
  690.             break;
  691.         case ASCII_TYPE:
  692.             tprintf("Ascii\n");
  693.             break;
  694.         case LOGICAL_TYPE:
  695.             tprintf("Logical bytesize %u\n",Ftp_logbsize);
  696.             break;
  697.         }
  698.         return 0;
  699.     }
  700.     switch(*argv[1]){
  701.     case 'i':
  702.     case 'I':
  703.     case 'b':
  704.     case 'B':
  705.         Ftp_type = IMAGE_TYPE;
  706.         break;
  707.     case 'a':
  708.     case 'A':
  709.         Ftp_type = ASCII_TYPE;
  710.         break;
  711.     case 'L':
  712.     case 'l':
  713.         Ftp_type = LOGICAL_TYPE;
  714.         Ftp_logbsize = atoi(argv[2]);
  715.         break;
  716.     default:
  717.         tprintf("Invalid type %s\n",argv[1]);
  718.         return 1;
  719.     }
  720.     return 0;
  721. }
  722.  
  723. /* Start receive transfer. Syntax: get <remote name> [<local name>] */
  724. static int
  725. doget(argc,argv,p)
  726. int argc;
  727. char *argv[];
  728. void *p;
  729. {
  730.     char *remotename,*localname;
  731.     register struct ftpcli *ftp;
  732.  
  733.     ftp = (struct ftpcli *)p;
  734.     if(ftp == NULLFTP){
  735.         tprintf(Notsess);
  736.         return 1;
  737.     }
  738.     remotename = argv[1];
  739.     if(argc < 3)
  740.         localname = remotename;
  741.     else
  742.         localname = argv[2];
  743.  
  744.     if(!ftp->update || compsub(ftp,localname,remotename) != 0)
  745.         getsub(ftp,"RETR",remotename,localname);
  746.     return 0;
  747. }
  748. /* Get a collection of files */
  749. static int
  750. domget(argc,argv,p)
  751. int argc;
  752. char *argv[];
  753. void *p;
  754. {
  755.     register struct ftpcli *ftp;
  756.     FILE *files, *filel;
  757.     char tmpname[80];
  758.     char *buf, *local, *c;
  759.     int i, inlist;
  760.     long r;
  761.  
  762.     if((ftp = (struct ftpcli *)p) == NULLFTP){
  763.         tprintf(Notsess);
  764.         return 1;
  765.     }
  766.     tmpnam(tmpname);
  767.     buf = mallocw(DIRBUF);
  768.     ftp->state = RECEIVING_STATE;
  769.     for(i=1;i<argc;i++){
  770.         if(argv[i][0] == '@'){
  771.             inlist = 1;
  772.             if((filel = fopen(make_fname(ftp->curdirs->dir,&argv[i][1]), "r")) == NULLFILE){
  773.                 tprintf("Can't open listfile: %s\n", &argv[i][1]);
  774.                 continue;
  775.             }
  776.             if((files = fopen(tmpname, "w")) == NULLFILE){
  777.                 tprintf("Can't open tempfile: %s\n", tmpname);
  778.                 continue;
  779.             }
  780.             while(fgets(buf,DIRBUF,filel) != NULLCHAR){
  781.                 fputs(buf,files);
  782.             }
  783.             fclose(files);
  784.             fclose(filel);
  785.             if((files = fopen(tmpname, "r")) == NULLFILE){
  786.                 tprintf("Can't open tempfile: %s\n", tmpname);
  787.                 continue;
  788.             }
  789.         } else {
  790.             inlist = 0;
  791.             r = getsub(ftp,"NLST",argv[i],tmpname);
  792.             if(ftp->abort)
  793.                 break;    /* Aborted */
  794.             if(r == -1 || (files = fopen(tmpname,"r")) == NULLFILE){
  795.                 tprintf("Can't NLST %s\n",argv[i]);
  796.                 unlink(tmpname);
  797.                 continue;
  798.             }
  799.         }
  800.         /* The tmp file now contains a list of the remote files, so
  801.          * go get 'em. Break out if the user signals an abort.
  802.          */
  803.         while(fgets(buf,DIRBUF,files) != NULLCHAR){
  804.             rip(buf);
  805.             local = strdup(buf);
  806. #ifdef    MSDOS
  807.             if(inlist){
  808.                 strrev(local);
  809.                 strtok(local, "\\/[]<>,?#~()&%");
  810.                 strrev(local);
  811.             }
  812.             if((c = strstr(local, ".")) != NULLCHAR) {
  813.                 c++;
  814.                 c = strtok(c, ".");    /* remove 2nd period if any*/
  815.             }
  816. #endif
  817.             if(!ftp->update || compsub(ftp,buf,buf) != 0)
  818.                 getsub(ftp,"RETR",buf,local);
  819.             usflush (ftp->control);
  820.             free(local);
  821.             if(ftp->abort){
  822.                 /* User abort */
  823.                 ftp->abort = 0;
  824.                 fclose(files);
  825.                 unlink(tmpname);
  826.                 free(buf);
  827.                 ftp->state = COMMAND_STATE;
  828.                 return 1;
  829.             }
  830.         }
  831.         fclose(files);
  832.         unlink(tmpname);
  833.     }
  834.     free(buf);
  835.     ftp->state = COMMAND_STATE;
  836.     ftp->abort = 0;
  837.     return 0;
  838. }
  839. /* Resume interrupted file transfer. Syntax: resume <remote name> [<local name>] */
  840. static int
  841. doresume(argc,argv,p)
  842. int argc;
  843. char *argv[];
  844. void *p;
  845. {
  846.     char *remotename,*localname;
  847.     register struct ftpcli *ftp;
  848.  
  849.     ftp = (struct ftpcli *)p;
  850.     if(ftp == NULLFTP){
  851.         tprintf(Notsess);
  852.         return 1;
  853.     }
  854.     remotename = argv[1];
  855.     if(argc < 3)
  856.         localname = remotename;
  857.     else
  858.         localname = argv[2];
  859.  
  860.     getsub(ftp,"RSME",remotename,localname);
  861.     return 0;
  862. }
  863. /* List remote directory. Syntax: dir <remote files> [<local name>] */
  864. static int
  865. dolist(argc,argv,p)
  866. int argc;
  867. char *argv[];
  868. void *p;
  869. {
  870.     char *remotename,*localname;
  871.     register struct ftpcli *ftp;
  872.  
  873.     ftp = (struct ftpcli *)p;
  874.     if(ftp == NULLFTP){
  875.         tprintf(Notsess);
  876.         return 1;
  877.     }
  878.     remotename = argv[1];
  879.     if(argc > 2)
  880.         localname = argv[2];
  881.     else
  882.         localname = NULLCHAR;
  883.  
  884.     getsub(ftp,"LIST",remotename,localname);
  885.     return 0;
  886. }
  887. /* Remote directory list, short form. Syntax: ls <remote files> [<local name>] */
  888. static int
  889. dols(argc,argv,p)
  890. int argc;
  891. char *argv[];
  892. void *p;
  893. {
  894.     char *remotename,*localname;
  895.     register struct ftpcli *ftp;
  896.  
  897.     ftp = (struct ftpcli *)p;
  898.     if(ftp == NULLFTP){
  899.         tprintf(Notsess);
  900.         return 1;
  901.     }
  902.     remotename = argv[1];
  903.     if(argc > 2)
  904.         localname = argv[2];
  905.     else
  906.         localname = NULLCHAR;
  907.  
  908.     getsub(ftp,"NLST",remotename,localname);
  909.     return 0;
  910. }
  911. static int
  912. domd5(argc,argv,p)
  913. int argc;
  914. char *argv[];
  915. void *p;
  916. {
  917.     char *remotename;
  918.     register struct ftpcli *ftp;
  919.     int control;
  920.     int resp;
  921.     int typewait = 0;
  922.  
  923.     ftp = (struct ftpcli *)p;
  924.     if(ftp == NULLFTP){
  925.         tprintf(Notsess);
  926.         return 1;
  927.     }
  928.     control = ftp->control;
  929.     remotename = argv[1];
  930.     if(ftp->typesent != ftp->type){
  931.         switch(ftp->type){
  932.         case ASCII_TYPE:
  933.             usprintf(control,"TYPE A\n");
  934.             break;
  935.         case IMAGE_TYPE:
  936.             usprintf(control,"TYPE I\n");
  937.             break;
  938.         case LOGICAL_TYPE:
  939.             usprintf(control,"TYPE L %d\n",ftp->logbsize);
  940.             break;
  941.         }
  942.         ftp->typesent = ftp->type;
  943.         if(!ftp->batch){
  944.             resp = getresp(ftp,200);
  945.             if(resp == -1 || resp > 299)
  946.                 goto failure;
  947.         } else
  948.             typewait = 1;
  949.  
  950.     }
  951.     usprintf(control,"XMD5 %s\n",remotename);
  952.     if(typewait)
  953.         (void)getresp(ftp,200);
  954.     (void)getresp(ftp,200);
  955. failure:;
  956.     return 0;
  957. }
  958. static int
  959. docompare(argc,argv,p)
  960. int argc;
  961. char *argv[];
  962. void *p;
  963. {
  964.     char *remotename,*localname;
  965.     register struct ftpcli *ftp;
  966.  
  967.     ftp = (struct ftpcli *)p;
  968.     if(ftp == NULLFTP){
  969.         tprintf(Notsess);
  970.         return 1;
  971.     }
  972.     remotename = argv[1];
  973.     if(argc > 2)
  974.         localname = argv[2];
  975.     else
  976.         localname = remotename;
  977.  
  978.     if(compsub(ftp,localname,remotename) == 0)
  979.         tprintf("Same\n");
  980.     else
  981.         tprintf("Different\n");
  982.     return 0;
  983. }
  984. /* Compare a collection of files */
  985. static int
  986. domcompare(argc,argv,p)
  987. int argc;
  988. char *argv[];
  989. void *p;
  990. {
  991.     register struct ftpcli *ftp;
  992.     FILE *files;
  993.     char *buf;
  994.     int i;
  995.     long r;
  996.     char tmpname[80];
  997.  
  998.     if((ftp = (struct ftpcli *)p) == NULLFTP){
  999.         tprintf(Notsess);
  1000.         return 1;
  1001.     }
  1002.     tmpnam(tmpname);
  1003.     buf = mallocw(DIRBUF);
  1004.     ftp->state = RECEIVING_STATE;
  1005.     for(i=1;i<argc;i++){
  1006.         r = getsub(ftp,"NLST",argv[i],tmpname);
  1007.         if(ftp->abort)
  1008.             break;    /* Aborted */
  1009.         if(r == -1 || (files = fopen(tmpname,"r")) == NULLFILE){
  1010.             tprintf("Can't NLST %s\n",argv[i]);
  1011.             unlink(tmpname);
  1012.             continue;
  1013.         }
  1014.         /* The tmp file now contains a list of the remote files, so
  1015.          * go get 'em. Break out if the user signals an abort.
  1016.          */
  1017.         while(fgets(buf,DIRBUF,files) != NULLCHAR){
  1018.             rip(buf);
  1019.             if(compsub(ftp,buf,buf) == 0)
  1020.                 tprintf("%s - Same\n",buf);
  1021.             else
  1022.                 tprintf("%s - Different\n",buf);
  1023.  
  1024.             if(ftp->abort){
  1025.                 /* User abort */
  1026.                 ftp->abort = 0;
  1027.                 fclose(files);
  1028.                 unlink(tmpname);
  1029.                 free(buf);
  1030.                 ftp->state = COMMAND_STATE;
  1031.                 return 1;
  1032.             }
  1033.         }
  1034.         fclose(files);
  1035.         unlink(tmpname);
  1036.     }
  1037.     free(buf);
  1038.     ftp->state = COMMAND_STATE;
  1039.     ftp->abort = 0;
  1040.     return 0;
  1041. }
  1042. /* Common subroutine to compare a local with a remote file
  1043.  * Return 1 if files are different, 0 if they are the same
  1044.  */
  1045. static int
  1046. compsub(ftp,localname,remotename)
  1047. struct ftpcli *ftp;
  1048. char *localname;
  1049. char *remotename;
  1050. {
  1051.     char *mode,*cp;
  1052.     FILE *fp;
  1053.     int control;
  1054.     int resp,i;
  1055.     int typewait = 0;
  1056.     char remhash[16];
  1057.     char lochash[16];
  1058.     char *file;
  1059.  
  1060.     control = ftp->control;
  1061.  
  1062.     switch(ftp->type){
  1063.     case IMAGE_TYPE:
  1064.     case LOGICAL_TYPE:
  1065.         mode = READ_BINARY;
  1066.         break;
  1067.     case ASCII_TYPE:
  1068.         mode = READ_TEXT;
  1069.         break;
  1070.     }
  1071.     if((fp = fopen(make_fname(ftp->curdirs->dir,localname),mode)) == NULLFILE){
  1072.         tprintf("Can't read local file %s\n",make_fname(ftp->curdirs->dir,localname));
  1073.         return 1;
  1074.     }
  1075.     if(ftp->typesent != ftp->type){
  1076.         switch(ftp->type){
  1077.         case ASCII_TYPE:
  1078.             usprintf(control,"TYPE A\n");
  1079.             break;
  1080.         case IMAGE_TYPE:
  1081.             usprintf(control,"TYPE I\n");
  1082.             break;
  1083.         case LOGICAL_TYPE:
  1084.             usprintf(control,"TYPE L %d\n",ftp->logbsize);
  1085.             break;
  1086.         }
  1087.         ftp->typesent = ftp->type;
  1088.         if(!ftp->batch){
  1089.             resp = getresp(ftp,200);
  1090.             if(resp == -1 || resp > 299)
  1091.                 goto failure;
  1092.         } else
  1093.             typewait = 1;
  1094.     }
  1095.     usprintf(control,"XMD5 %s\n",remotename);
  1096.     /* Try to overlap the two MD5 operations */
  1097.     md5hash(fp,lochash,ftp->type == ASCII_TYPE);
  1098.     fclose(fp);
  1099.     if(typewait && (resp = getresp(ftp,200)) > 299)
  1100.         goto failure;
  1101.     if((resp = getresp(ftp,200)) > 299){
  1102.         if(resp == 500)
  1103.             ftp->update = 0;    /* XMD5 not supported */
  1104.         goto failure;
  1105.     }    
  1106.     if((cp = strchr(ftp->line,' ')) == NULLCHAR){
  1107.         tprintf("Error in response\n");
  1108.         goto failure;
  1109.     }
  1110.     /* Convert ascii/hex back to binary */
  1111.     readhex(remhash,cp,sizeof(remhash));
  1112.     if(ftp->verbose > 1){
  1113.         tprintf("Loc ");
  1114.         for(i=0;i<sizeof(lochash);i++)
  1115.             tprintf("%02x",lochash[i] & 0xff);
  1116.         tprintf(" %s\n",make_fname(ftp->curdirs->dir,localname));
  1117.     }
  1118.     if(memcmp(lochash,remhash,sizeof(remhash)) == 0)
  1119.         return 0;
  1120.     else
  1121.         return 1;
  1122. failure:;
  1123.     return 1;
  1124. }
  1125.  
  1126.  
  1127. /* Common code to LIST/NLST/RETR/RSME and mget
  1128.  * Returns number of bytes received if successful
  1129.  * Returns -1 on error
  1130.  */
  1131. static long
  1132. getsub(ftp,command,remotename,localname)
  1133. register struct ftpcli *ftp;
  1134. char *command,*remotename,*localname;
  1135. {
  1136.     unsigned long total;
  1137.     FILE *fp;
  1138.     int cnt,resp,i,control,savmode;
  1139.     char *mode;
  1140.     struct sockaddr_in lsocket;
  1141.     struct sockaddr_in lcsocket;
  1142.     int32 startclk,rate;
  1143.     int vsave;
  1144.     int typewait = 0;
  1145.     int prevstate;
  1146.     unsigned long starting;
  1147.  
  1148.     if(ftp == NULLFTP)
  1149.         return -1;
  1150.     control = ftp->control;
  1151.     savmode = ftp->type;
  1152.  
  1153.     switch(ftp->type){
  1154.     case IMAGE_TYPE:
  1155.     case LOGICAL_TYPE:
  1156.         if(strcmp(command,"RSME") == 0)
  1157.             mode = APPEND_BINARY;
  1158.         else
  1159.             mode = WRITE_BINARY;
  1160.         break;
  1161.     case ASCII_TYPE:
  1162.         if(strcmp(command,"RSME") == 0)
  1163.             mode = APPEND_TEXT;
  1164.         else
  1165.             mode = WRITE_TEXT;
  1166.         break;
  1167.     }
  1168.     /* Open the file */
  1169.     if(localname == NULLCHAR){
  1170.         fp = NULLFILE;
  1171.     } else if((fp = fopen(make_fname(ftp->curdirs->dir,localname),mode)) == NULLFILE){
  1172.         tprintf("Can't write %s: %s\n",localname,sys_errlist[errno]);
  1173.         return -1;
  1174.     }
  1175.     /* Open the data connection */
  1176.     ftp->data = socket(AF_INET,SOCK_STREAM,0);
  1177.     listen(ftp->data,0);    /* Accept only one connection */
  1178.     prevstate = ftp->state;
  1179.     ftp->state = RECEIVING_STATE;
  1180. #ifdef LZW
  1181.     if (ftp->lzw)
  1182.         lzwinit (ftp->data, ftp->lzwbits, ftp->lzwmode);    
  1183. #endif
  1184.  
  1185.     /* Send TYPE message, if necessary */
  1186.     if(strcmp(command,"LIST") == 0 || strcmp(command,"NLST") == 0){
  1187.         /* Directory listings are always in ASCII */
  1188.         ftp->type = ASCII_TYPE;
  1189.     }
  1190.     if(ftp->typesent != ftp->type){
  1191.         switch(ftp->type){
  1192.         case ASCII_TYPE:
  1193.             usprintf(control,"TYPE A\n");
  1194.             break;
  1195.         case IMAGE_TYPE:
  1196.             usprintf(control,"TYPE I\n");
  1197.             break;
  1198.         case LOGICAL_TYPE:
  1199.             usprintf(control,"TYPE L %d\n",ftp->logbsize);
  1200.             break;
  1201.         }
  1202.         ftp->typesent = ftp->type;
  1203.         if(!ftp->batch){
  1204.             resp = getresp(ftp,200);
  1205.             if(resp == -1 || resp > 299)
  1206.                 goto failure;
  1207.         } else
  1208.             typewait = 1;
  1209.     }
  1210.     /* Send the PORT message. Use the IP address
  1211.      * on the local end of our control connection.
  1212.      */
  1213.     i = SOCKSIZE;
  1214.     getsockname(ftp->data,(char *)&lsocket,&i); /* Get port number */
  1215.     i = SOCKSIZE;
  1216.     getsockname(ftp->control,(char *)&lcsocket,&i);
  1217.     lsocket.sin_addr.s_addr = lcsocket.sin_addr.s_addr;
  1218.     sendport(control,&lsocket);
  1219.     if(!ftp->batch){
  1220.         /* Get response to PORT command */
  1221.         resp = getresp(ftp,200);
  1222.         if(resp == -1 || resp > 299)
  1223.             goto failure;
  1224.     }
  1225.     /* Generate the command to start the transfer */
  1226.     if(remotename != NULLCHAR)
  1227.         usprintf(control,"%s %s\n",command,remotename);
  1228.     else
  1229.         usprintf(control,"%s\n",command);
  1230.  
  1231.     if(ftp->batch){
  1232.         /* Get response to TYPE command, if sent */
  1233.         if(typewait){
  1234.             resp = getresp(ftp,200);
  1235.             if(resp == -1 || resp > 299)
  1236.                 goto failure;
  1237.         }
  1238.         /* Get response to PORT command */
  1239.         resp = getresp(ftp,200);
  1240.         if(resp == -1 || resp > 299)
  1241.             goto failure;
  1242.     }
  1243.     /* Get the intermediate "150" response */
  1244.     resp = getresp(ftp,100);
  1245.     if(resp == -1 || resp >= 400)
  1246.         goto failure;
  1247.  
  1248.     /* Wait for the server to open the data connection */
  1249.     cnt = 0;
  1250.     accept(ftp->data,NULLCHAR,(int *)NULL);
  1251.     startclk = msclock();
  1252.  
  1253.     /* If output is to the screen, temporarily disable hash marking */
  1254.     vsave = ftp->verbose;
  1255.     if(vsave >= V_HASH && fp == NULLFILE)
  1256.         ftp->verbose = V_NORMAL;
  1257.     if(strcmp(command,"RSME") == 0){
  1258.         if((starting = getsize(fp)) == -1)
  1259.             starting = 0L;
  1260.         usprintf(control,"%lu %lu\n",starting,checksum(fp,starting));
  1261.         usflush(control);
  1262.         fseek(fp,starting,SEEK_SET);
  1263.     }
  1264.     total = recvfile(fp,ftp->data,ftp->type,(ftp->verbose >= V_HASH) ? ftp->verbose : 0);
  1265.     /* Immediately close the data connection; some servers (e.g., TOPS-10)
  1266.      * wait for the data connection to close completely before returning
  1267.      * the completion message on the control channel
  1268.      */
  1269.     close_s(ftp->data);
  1270.     ftp->data = -1;
  1271.  
  1272. #ifdef    CPM
  1273.     if(fp != NULLFILE && ftp->type == ASCII_TYPE)
  1274.         putc(CTLZ,fp);
  1275. #endif
  1276.     if(fp != NULLFILE && fp != stdout)
  1277.         fclose(fp);
  1278.     if(remotename == NULLCHAR)
  1279.         remotename = "";
  1280.     if(total == -1){
  1281.         tprintf("%s %s: Error/abort during data transfer\n",command,remotename);
  1282.     } else if(ftp->verbose >= V_SHORT){
  1283.         startclk = msclock() - startclk;
  1284.         rate = 0;
  1285.         if(startclk != 0){    /* Avoid divide-by-zero */
  1286.             if(total < 4294967L){
  1287.                 rate = (total*1000)/startclk;
  1288.             } else {    /* Avoid overflow */
  1289.                 rate = total/(startclk/1000);
  1290.             }
  1291.         }
  1292.         tprintf("%s %s: %lu bytes in %lu sec (%lu/sec)\n",
  1293.          command,remotename, total,startclk/1000,rate);
  1294.     }
  1295.     /* Get the "Sent" message */
  1296.     getresp(ftp,200);
  1297.  
  1298.     ftp->state = prevstate;
  1299.     ftp->verbose = vsave;
  1300.     ftp->type = savmode;
  1301.     return total;
  1302.  
  1303. failure:
  1304.     /* Error, quit */
  1305.     if(fp != NULLFILE && fp != stdout)
  1306.         fclose(fp);
  1307.     close_s(ftp->data);
  1308.     ftp->data = -1;
  1309.     ftp->state = prevstate;
  1310.     ftp->type = savmode;
  1311.     return -1;
  1312. }
  1313. /* Send a file. Syntax: put <local name> [<remote name>] */
  1314. static int
  1315. doput(argc,argv,p)
  1316. int argc;
  1317. char *argv[];
  1318. void *p;
  1319. {
  1320.     register struct ftpcli *ftp;
  1321.     char *remotename,*localname;
  1322.  
  1323.     if((ftp = (struct ftpcli *)p) == NULLFTP){
  1324.         tprintf(Notsess);
  1325.         return 1;
  1326.     }
  1327.     localname = argv[1];
  1328.     if(argc < 3)
  1329.         remotename = localname;
  1330.     else
  1331.         remotename = argv[2];
  1332.  
  1333.     if(!ftp->update || compsub(ftp,localname,remotename) != 0)
  1334.         putsub(ftp,remotename,localname,(*argv[0] == 'a') ? 2 : 0);
  1335.     return 0;
  1336. }
  1337. /* Put a collection of files */
  1338. static int
  1339. domput(argc,argv,p)
  1340. int argc;
  1341. char *argv[];
  1342. void *p;
  1343. {
  1344.     register struct ftpcli *ftp;
  1345.     FILE *files;
  1346.     int i, j;
  1347.     char tmpname[80];    
  1348.     char *buf, *file;
  1349.  
  1350.     if((ftp = (struct ftpcli *)p) == NULLFTP){
  1351.         tprintf(Notsess);
  1352.         return 1;
  1353.     }
  1354.     tmpnam(tmpname);
  1355.     if((files = fopen(tmpname,"w+")) == NULLFILE){
  1356.         tprintf("Can't list local files\n");
  1357.         unlink(tmpname);
  1358.         return 1;
  1359.     }
  1360.     for(i=1;i<argc;i++)    {
  1361.         /* Use the path in the ftp client struct, since user may have done
  1362.          * a lcd command to change dir !
  1363.          */
  1364.         file = pathname(ftp->curdirs->dir,argv[i]);
  1365.         /* Shift everything back one byte, pathname returns with a leading '/'! */
  1366.         for(j=1;j<=strlen(file);j++)
  1367.             file[j-1] = file[j];
  1368.         getdir(file,0,files);
  1369.         free(file);
  1370.     }
  1371.  
  1372.     rewind(files);
  1373.     buf = mallocw(DIRBUF);
  1374.     ftp->state = SENDING_STATE;
  1375.     while(fgets(buf,DIRBUF,files) != NULLCHAR){
  1376.         rip(buf);
  1377.         if(!ftp->update || compsub(ftp,buf,buf) != 0)
  1378.             putsub(ftp,buf,buf,0);
  1379.         if(ftp->abort)
  1380.             break;        /* User abort */
  1381.         usflush (ftp->control);
  1382.     }
  1383.     fclose(files);
  1384.     unlink(tmpname);
  1385.     free(buf);
  1386.     ftp->state = COMMAND_STATE;
  1387.     ftp->abort = 0;
  1388.     return 0;
  1389. }
  1390. /* Put a file, appending data to it - iw0cnb */
  1391. static int
  1392. dorput(argc,argv,p)
  1393. int argc;
  1394. char *argv[];
  1395. void *p;
  1396. {
  1397.     register struct ftpcli *ftp;
  1398.     char *remotename,*localname;
  1399.  
  1400.     if((ftp = (struct ftpcli *)p) == NULLFTP){
  1401.         tprintf(Notsess);
  1402.         return 1;
  1403.     }
  1404.     localname = argv[1];
  1405.     if(argc < 3)
  1406.         remotename = localname;
  1407.     else
  1408.         remotename = argv[2];
  1409.  
  1410.     putsub(ftp,remotename,localname,1);
  1411.     return 0;
  1412. }
  1413. /* Common code to put, mput.
  1414.  * Returns number of bytes sent if successful
  1415.  * Returns -1 on error
  1416.  */
  1417. static long
  1418. putsub(ftp,remotename,localname,putr)
  1419. register struct ftpcli *ftp;
  1420. char *remotename,*localname;
  1421. int putr;       /* Flag: 0 if standard put, 1 if put with resume, if 2 append */
  1422. {
  1423.     char *mode;
  1424.     int i,resp,control;
  1425.     unsigned long total;
  1426.     FILE *fp;
  1427.     struct sockaddr_in lsocket,lcsocket;
  1428.     int32 startclk,rate;
  1429.     int typewait = 0;
  1430.     int prevstate;
  1431.     char *line;
  1432.     unsigned long starting;
  1433.     unsigned long check, local_check;
  1434.  
  1435.     control = ftp->control;
  1436.     if(ftp->type == IMAGE_TYPE)
  1437.         mode = READ_BINARY;
  1438.     else
  1439.         mode = READ_TEXT;
  1440.  
  1441.     /* Open the file */
  1442.     if((fp = fopen(make_fname(ftp->curdirs->dir,localname),mode)) == NULLFILE){
  1443.         tprintf("Can't read %s: %s\n",localname,sys_errlist[errno]);
  1444.         return -1;
  1445.     }
  1446.     if(ftp->type == ASCII_TYPE && isbinary(fp)){
  1447.         tprintf("Warning: type is ASCII and %s appears to be binary\n",localname);
  1448.     }
  1449.     /* Open the data connection */
  1450.     ftp->data = socket(AF_INET,SOCK_STREAM,0);
  1451.     listen(ftp->data,0);
  1452.     prevstate = ftp->state;
  1453.     ftp->state = SENDING_STATE;
  1454. #ifdef LZW
  1455.     if (ftp->lzw)
  1456.         lzwinit (ftp->data, ftp->lzwbits, ftp->lzwmode);    
  1457. #endif
  1458.  
  1459.     /* Send TYPE message, if necessary */
  1460.     if(ftp->typesent != ftp->type){
  1461.         switch(ftp->type){
  1462.         case ASCII_TYPE:
  1463.             usprintf(control,"TYPE A\n");
  1464.             break;
  1465.         case IMAGE_TYPE:
  1466.             usprintf(control,"TYPE I\n");
  1467.             break;
  1468.         case LOGICAL_TYPE:
  1469.             usprintf(control,"TYPE L %d\n",ftp->logbsize);
  1470.             break;
  1471.         }
  1472.         ftp->typesent = ftp->type;
  1473.  
  1474.         /* Get response to TYPE command */
  1475.         if(!ftp->batch){
  1476.             resp = getresp(ftp,200);
  1477.             if(resp == -1 || resp > 299){
  1478.                 goto failure;
  1479.             }
  1480.         } else
  1481.             typewait = 1;
  1482.     }
  1483.     /* Send the PORT message. Use the IP address
  1484.      * on the local end of our control connection.
  1485.      */
  1486.     i = SOCKSIZE;
  1487.     getsockname(ftp->data,(char *)&lsocket,&i);
  1488.     i = SOCKSIZE;
  1489.     getsockname(ftp->control,(char *)&lcsocket,&i);
  1490.     lsocket.sin_addr.s_addr = lcsocket.sin_addr.s_addr;
  1491.     sendport(control,&lsocket);
  1492.     if(!ftp->batch){
  1493.         /* Get response to PORT command */
  1494.         resp = getresp(ftp,200);
  1495.         if(resp == -1 || resp > 299){
  1496.             goto failure;
  1497.         }
  1498.     }
  1499.     /* Generate the command to start the transfer */
  1500.     if(putr == 1)
  1501.         usprintf(control,"RPUT %s\n",remotename);
  1502.     else if (putr == 2)
  1503.         usprintf(control,"APPE %s\n",remotename);
  1504.     else
  1505.         usprintf(control,"STOR %s\n",remotename);
  1506.  
  1507.     if(ftp->batch){
  1508.         /* Get response to TYPE command, if sent */
  1509.         if(typewait){
  1510.             resp = getresp(ftp,200);
  1511.             if(resp == -1 || resp > 299){
  1512.                 goto failure;
  1513.             }
  1514.         }
  1515.         /* Get response to PORT command */
  1516.         resp = getresp(ftp,200);
  1517.         if(resp == -1 || resp > 299){
  1518.             goto failure;
  1519.         }
  1520.     }
  1521.     /* Get the intermediate "150" response */
  1522.     resp = getresp(ftp,100);
  1523.     if(resp == -1 || resp >= 400){
  1524.         goto failure;
  1525.     }
  1526.  
  1527.     /* Wait for the data connection to open. Otherwise the first
  1528.      * block of data would go out with the SYN, and this may confuse
  1529.      * some other TCPs
  1530.      */
  1531.     accept(ftp->data,NULLCHAR,(int *)NULL);
  1532.  
  1533.     startclk = msclock();
  1534.  
  1535.     if(putr == 1){          /* Wait for file offset and checksum */
  1536.         line=mallocw(40);
  1537.         recvline(control,line,40);
  1538.         starting=atol(line);
  1539.         check = (unsigned long)atol(strchr(line,' '));
  1540.         free(line);
  1541.         local_check = checksum(fp,starting);
  1542.         if(ftp->verbose >= V_HASH)
  1543.             tprintf("Remote checksum: %lu - Local checksum: %lu - Offset: %lu\n",check,local_check,starting);
  1544.         check -= local_check;
  1545.         if(check != 0){
  1546.             tprintf("Can't send %s: files are different\n",localname);
  1547.             shutdown(ftp->data,1);
  1548.             getresp(ftp,200);
  1549.             goto failure;
  1550.         }
  1551.     }
  1552.  
  1553.     total = sendfile(fp,ftp->data,ftp->type,(ftp->verbose >= V_HASH) ? ftp->verbose : 0);
  1554.     close_s(ftp->data);
  1555.     ftp->data = -1;
  1556.     fclose(fp);
  1557.  
  1558.     /* Wait for control channel ack before calculating transfer time;
  1559.      * this accounts for transmitted data in the pipe.
  1560.      */
  1561.     getresp(ftp,200);
  1562.  
  1563.     if(total == -1){
  1564.         tprintf("STOR %s: Error/abort during data transfer\n",remotename);
  1565.     } else if(ftp->verbose >= V_SHORT){
  1566.         startclk = msclock() - startclk;
  1567.         rate = 0;
  1568.         if(startclk != 0){    /* Avoid divide-by-zero */
  1569.             if(total < 4294967L){
  1570.                 rate = (total*1000)/startclk;
  1571.             } else {    /* Avoid overflow */
  1572.                 rate = total/(startclk/1000);
  1573.             }
  1574.         }
  1575.         tprintf("STOR %s: %lu bytes in %lu sec (%lu/sec)\n",
  1576.          remotename,total,startclk/1000,rate);
  1577.     }
  1578.     ftp->state = prevstate;
  1579.     return total;
  1580.  
  1581. failure:
  1582.     /* Error, quit */
  1583.     fclose(fp);
  1584.     close_s(ftp->data);
  1585.     ftp->data = -1;
  1586.     ftp->state = prevstate;
  1587.     return -1;
  1588. }
  1589. /* Abort a GET or PUT operation in progress. Note: this will leave
  1590.  * the partial file on the local or remote system
  1591.  */
  1592. int
  1593. doabort(argc,argv,p)
  1594. int argc;
  1595. char *argv[];
  1596. void *p;
  1597. {
  1598.     register struct session *sp;
  1599.     register struct ftpcli *ftp;
  1600.  
  1601.     sp = (struct session *)p;
  1602.     if(sp == NULLSESSION)
  1603.         return -1;
  1604.  
  1605.     /* Default is the current session, but it can be overridden with
  1606.      * an argument.
  1607.      */
  1608.     if(argc > 1)
  1609.         sp = sessptr(argv[1]);
  1610.  
  1611.     if(sp == NULLSESSION || sp->type != FTP){
  1612.         tprintf("Not an active FTP session\n");
  1613.         return 1;
  1614.     }
  1615.     ftp = sp->cb.ftp;
  1616.     switch(ftp->state){
  1617.     case COMMAND_STATE:
  1618.         tprintf("No active transfer\n");
  1619.         return 1;
  1620.     case SENDING_STATE:
  1621.         /* Send a premature EOF.
  1622.          * Unfortunately we can't just reset the connection
  1623.          * since the remote side might end up waiting forever
  1624.          * for us to send something.
  1625.          */
  1626.         shutdown(ftp->data,1);    /* Note fall-thru */
  1627.         ftp->abort = 1;
  1628.         break;
  1629.     case RECEIVING_STATE:
  1630.         /* Just blow away the receive socket */
  1631.         shutdown(ftp->data,2);    /* Note fall-thru */
  1632.         ftp->abort = 1;
  1633.         break;
  1634.     }
  1635.     return 0;
  1636. }
  1637. /* send PORT message */
  1638. static void
  1639. sendport(s,socket)
  1640. int s;
  1641. struct sockaddr_in *socket;
  1642. {
  1643.     /* Send PORT a,a,a,a,p,p message */
  1644.     usprintf(s,"PORT %u,%u,%u,%u,%u,%u\n",
  1645.         hibyte(hiword(socket->sin_addr.s_addr)),
  1646.         lobyte(hiword(socket->sin_addr.s_addr)),
  1647.         hibyte(loword(socket->sin_addr.s_addr)),
  1648.         lobyte(loword(socket->sin_addr.s_addr)),
  1649.         hibyte(socket->sin_port),
  1650.         lobyte(socket->sin_port));
  1651. }
  1652.  
  1653. /* Wait for, read and display response from FTP server. Return the result code.
  1654.  */
  1655. static int
  1656. getresp(ftp,mincode)
  1657. struct ftpcli *ftp;
  1658. int mincode;    /* Keep reading until at least this code comes back */
  1659. {
  1660.     int rval;
  1661.  
  1662.     usflush(ftp->control);
  1663.     for(;;){
  1664.         /* Get line */
  1665.         if(recvline(ftp->control,ftp->line,LINELEN) == -1){
  1666.             rval = -1;
  1667.             break;
  1668.         }
  1669.         rip(ftp->line);        /* Remove cr/lf */
  1670.         rval = atoi(ftp->line);
  1671.         if(rval >= 400 || ftp->verbose >= V_NORMAL)
  1672.             tprintf("%s\n",ftp->line);    /* Display to user */
  1673.  
  1674.         /* Messages with dashes are continued */
  1675.         if(ftp->line[3] != '-' && rval >= mincode)
  1676.             break;
  1677.     }
  1678.     return rval;
  1679. }
  1680.  
  1681. /* Issue a prompt and read a line from the user */
  1682. static int
  1683. getline(sp,prompt,buf,n)
  1684. struct session *sp;
  1685. char *prompt;
  1686. char *buf;
  1687. int n;
  1688. {
  1689.     /* If there's something already there, don't issue prompt */
  1690.     if(socklen(sp->input,0) == 0)
  1691.         tprintf(prompt);
  1692.  
  1693.     usflush(sp->output);
  1694.     return recvline(sp->input,buf,n);
  1695. }
  1696.  
  1697. /* Attempt to log in the user whose name is in ftp->username and password
  1698.  * in pass
  1699.  */
  1700. static char *
  1701. ftpcli_login(ftp,host)
  1702. struct ftpcli *ftp;
  1703. char *host; {
  1704.  
  1705.     char buf[80],*cp,*cp1;
  1706.     FILE *fp;
  1707.  
  1708.     extern char *Hostfile;    /* List of user names and permissions */
  1709.  
  1710.     if((fp = fopen(Hostfile,"r")) == NULLFILE){
  1711.         return NULLCHAR;
  1712.     }
  1713.     while(fgets(buf,sizeof(buf),fp),!feof(fp)){
  1714.         buf[strlen(buf)-1] = '\0';    /* Nuke the newline */
  1715.         if(buf[0] == '#')
  1716.             continue;    /* Comment */
  1717.         if((cp = strchr(buf,' ')) == NULLCHAR)
  1718.             /* Bogus entry */
  1719.             continue;
  1720.         *cp++ = '\0';        /* Now points to user name */
  1721.         if(strcmp(host,buf) == 0)
  1722.             break;        /* Found host name */
  1723.     }
  1724.     if(feof(fp)){
  1725.         /* User name not found in file */
  1726.         fclose(fp);
  1727.         return NULLCHAR;
  1728.     }
  1729.     fclose(fp);
  1730.     /* Look for space after user field in file */
  1731.     if((cp1 = strchr(cp,' ')) == NULLCHAR)
  1732.         /* if not there then we'll prompt */
  1733.         ftp->password = NULLCHAR;
  1734.     else
  1735.         *cp1++ = '\0';        /* Now points to password */
  1736.         if(strcmp(cp,"*") == 0)
  1737.             cp1 = "anonymous";
  1738.         ftp->password = strdup(cp1);
  1739.     return strdup(cp);
  1740. }
  1741.  
  1742. int
  1743. dolcd(argc,argv,p)
  1744. int argc;
  1745. char *argv[];
  1746. void *p;
  1747. {
  1748.     register struct ftpcli *ftp;
  1749.  
  1750.     ftp = (struct ftpcli *)p;
  1751.     if(ftp == NULLFTP){
  1752.         tprintf(Notsess);
  1753.         return 1;
  1754.     }
  1755.  
  1756.     if(argc > 1){
  1757.         if (!dir_ok(argv[1],ftp->curdirs)) {
  1758.              tprintf("Invalid Drive/Directory - %s\n",argv[1]);
  1759.              return 1;
  1760.         }
  1761.            
  1762.     }
  1763.     tprintf("Local Directory - %s\n",ftp->curdirs->dir);
  1764.     
  1765.     return 0;
  1766. }
  1767.  
  1768.  
  1769. int
  1770. doldir(argc,argv,p)
  1771. int argc;
  1772. char *argv[];
  1773. void *p;
  1774. {
  1775.     register struct ftpcli *ftp;
  1776.     char ** margv;
  1777.     char path[128],*q;
  1778.  
  1779.     margv=(char **)callocw(2,sizeof(char*));
  1780.  
  1781.     ftp = (struct ftpcli *)p;
  1782.     if(ftp == NULLFTP){
  1783.         tprintf(Notsess);
  1784.         return 1;
  1785.     }
  1786.     
  1787.     tprintf("\n");
  1788.     if (argc == 1)
  1789.         margv[1]=strdup(ftp->curdirs->dir);
  1790.     else
  1791.         margv[1]=strdup(make_dir_path(argc,argv[1],ftp->curdirs->dir));
  1792.     dodir(2,margv,p);
  1793.     free(margv[1]);
  1794.     free(margv);
  1795.     tprintf("\n");
  1796.     return 0;
  1797. }
  1798.  
  1799. int
  1800. dolmkdir(argc,argv,p)
  1801. int argc;
  1802. char *argv[];
  1803. void *p;
  1804. {
  1805.     register struct ftpcli *ftp;
  1806.     char *buf;
  1807.  
  1808.     ftp = (struct ftpcli *)p;
  1809.     if(ftp == NULLFTP){
  1810.         tprintf(Notsess);
  1811.         return 1;
  1812.     }
  1813. /*    undosify(argv[1]);    done in make_fname    */
  1814.     buf=strdup(make_fname(ftp->curdirs->dir,argv[1]));
  1815.     if (mkdir(buf) == -1)
  1816.         tprintf("Can't make %s: %s\n",buf,sys_errlist[errno]);
  1817.     else
  1818.         tprintf("Directory %s Created\n",buf);
  1819.     free(buf);
  1820.     return 0;
  1821. }                
  1822.  
  1823. int
  1824. dolrmdir(argc,argv,p)
  1825. int argc;
  1826. char *argv[];
  1827. void *p;
  1828. {
  1829.     register struct ftpcli *ftp;
  1830.     char *buf;
  1831.  
  1832.     ftp = (struct ftpcli *)p;
  1833.     if(ftp == NULLFTP){
  1834.         tprintf(Notsess);
  1835.         return 1;
  1836.     }
  1837.     buf=strdup(make_fname(ftp->curdirs->dir,argv[1]));
  1838.     if (rmdir(buf) == -1)
  1839.         tprintf("Can't remove %s: %s\n",buf,sys_errlist[errno]);
  1840.     else
  1841.         tprintf("Directory %s Removed\n",buf);
  1842.     free(buf);
  1843.     return 0;
  1844. }                
  1845.  
  1846. #endif
  1847.